package com.cjq.jwt;


import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Import;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Date;
import java.util.Map;

@Slf4j
@Import(JwtAutoConfiguration.class)
public class JwtUtil {

    private final JwtProperties jwtProperties;

    // 缓存解码后的密钥字节数组
    private final byte[] decodedSecretKeyBytes;

    /**
     * 初始化JWT工具类
     *
     * @param jwtProperties 包含JWT配置属性的对象，用于获取Base64编码的密钥字符串
     * @throws IllegalArgumentException 当提供的密钥字符串不是有效的Base64编码时抛出
     */
    public JwtUtil(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
        try {
            // 解码Base64编码的密钥字符串为字节数组
            this.decodedSecretKeyBytes = Base64.getDecoder().decode(jwtProperties.getSecretKey());
        } catch (IllegalArgumentException e) {
            // 包装并抛出包含原始异常信息的非法参数异常
            throw new IllegalArgumentException("Invalid Base64 encoded JWT secret key", e);
        }
    }


    /**
     * 生成指定长度的随机密钥字符串, 请妥善保管
     *
     * @param length 密钥字节的长度（建议为16字节以上以确保安全性）
     * @return Base64编码的随机密钥字符串，请妥善保管
     */
    public static String getSecretStrKey(int length) {
        byte[] keyBytes = new byte[length];
        new SecureRandom().nextBytes(keyBytes);
        return Base64.getEncoder().encodeToString(keyBytes);
    }

    /**
     * 生成 HMAC-SHA256 算法签名的紧凑型JWT字符串（未压缩）
     *
     * @param jtl     令牌唯一标识符（JWT ID），用于标识令牌唯一性
     * @param claims  自定义声明集合，包含需要嵌入令牌的业务数据
     * @return        生成 HMAC-SHA256 算法签名的紧凑型JWT字符串（未压缩）
     */
    public String generateToken(String jtl, Map<String, Object> claims) {
        return generateToken(jtl, claims, SignatureAlgorithm.HS256);
    }

    /**
     * 生成 指定签名算法 的紧凑型JWT字符串
     *
     * @param jtl               JWT令牌唯一标识（如JWT ID）
     * @param claims            要包含在token中的声明（payload内容）
     * @param signatureAlgorithm 签名算法（如SignatureAlgorithm.HS256）
     * @return 生成 指定签名算法 的紧凑型JWT字符串
     */
    public String generateToken(String jtl, Map<String, Object> claims, SignatureAlgorithm signatureAlgorithm) {
        return generateToken(jtl, claims, signatureAlgorithm, null);
    }

    /**
     * 生成 指定算法签名的压缩的 JWT字符串
     *
     * @param jtl JWT的负载内容，通常包含基础用户信息或会话标识
     * @param claims 包含自定义声明的键值对集合，用于扩展令牌的附加信息
     * @param signatureAlgorithm 用于生成数字签名的算法（如SignatureAlgorithm.HS256）
     * @param compressionCodec 用于压缩令牌内容的编解码器（如CompressionCodec.GZIP）
     * @return 生成指定算法签名的压缩的JWT字符串
     */
    public String generateToken(String jtl, Map<String, Object> claims, SignatureAlgorithm signatureAlgorithm, CompressionCodec compressionCodec) {
        JwtBuilder jwtBuilder = commonToken(jtl, claims);
        if (compressionCodec != null) {
            jwtBuilder.compressWith(compressionCodec);
        }
        return jwtBuilder
                .signWith(generateSignKey(signatureAlgorithm.getJcaName()), signatureAlgorithm)
                .compact();
    }

    /**
     * 解析JWT令牌并返回包含声明的Claims对象。
     *
     * @param token              需要解析的JWT令牌字符串
     * @param signatureAlgorithm 用于验证JWT签名的签名算法
     * @return 解析后的Claims对象，包含JWT的有效载荷信息
     * @throws JwtException      当令牌解析失败或签名验证不通过时抛出
     */
    public Claims parseToken(String token, SignatureAlgorithm signatureAlgorithm) {
        return getJws(token, signatureAlgorithm).getBody();
    }

    /**
     * 检查给定的JWT Claims是否已过期，并返回剩余有效时间或状态码。
     *
     * @param claims JWT解析后的声明对象，包含过期时间信息
     * @return 如果token无效或已过期返回-1；否则返回过期时间的Unix时间戳（秒级）
     */
    public long isExpiredToken(Claims claims) {
        if (claims == null) {
            return -1;
        }
        try {
            Date expiration = claims.getExpiration();
            if (expiration == null) {
                return -1;
            }
            long expirationTime = expiration.getTime();
            long now = System.currentTimeMillis();
            if (now >= expirationTime) {
                return -1;
            } else {
                return expirationTime / 1000;
            }
        } catch (Exception e) {
            log.error("isExpiredToken(Claims claims)发生异常", e);
            return -1;
        }
    }

    private Jws<Claims> getJws(String token, SignatureAlgorithm signatureAlgorithm) {
        return Jwts.parserBuilder()
                .setSigningKey(generateSignKey(signatureAlgorithm.getJcaName()))
                .build()
                .parseClaimsJws(token);
    }

    private JwtBuilder commonToken(String jtl, Map<String, Object> claims) {
        long currentTimeMillis = System.currentTimeMillis();
        return Jwts.builder()
                .setIssuer(jwtProperties.getIssUser())
                .setSubject(jwtProperties.getSubject())
                .setAudience(jwtProperties.getAudience())
                .setExpiration(new Date(currentTimeMillis + jwtProperties.getExpiration() * 1000L))
                .setId(jtl)
                .addClaims(claims);
    }

    private SecretKey generateSignKey(String algorithm) {
        return new SecretKeySpec(decodedSecretKeyBytes, algorithm);
    }


    public static void main(String[] args) {
        // 示例：生成解码后长度为32字节的Base64密钥（HS256算法要求）
        String base64Key = getSecretStrKey(32);
        System.out.println("Base64 Key: " + base64Key);

        // 验证解码后的长度
        byte[] decodedBytes = Base64.getDecoder().decode(base64Key);
        System.out.println("Decoded Length: " + decodedBytes.length + " bytes");

        System.out.println(getSecretStrKey(34));
    }


}
